/*
 * Copyright (C) 2010 M. Hautle.
 * 
 * This file is part of SimpleEL.
 * 
 * SimpleEL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * SimpleEL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with SimpleEL. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.simpleel.internal.asm;

import java.beans.*;
import java.lang.reflect.*;
import java.util.*;
import javax.swing.*;
import ch.simpleel.data.*;

/**
 * Speed test for {@link IPropertyAccessor}s.
 * 
 * @author M. Hautle
 */
public class PropertyAccessorDemo {
    /**
     * Comment for <code>CALLS</code>
     */
    private static final int CALLS = 1000000;

    /**
     * Comment for <code>ROUNDS</code>
     */
    private static final int ROUNDS = 100;

    public static void main(String[] args) throws Exception {
        // buildNative(TheDom.class);
        // buildNative(JPanel.class);
        runTest(new TheDom(), nativeRun(new TheDom()));
        runTest(new TheDom(), nativeRun(new TheDom()));
        // runTest(new JPanel(), nativeRun(new JPanel()));
        // runTest(new JPanel(), nativeRun(new JPanel()));
    }

    /**
     * @param a
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IntrospectionException
     */
    private static void runTest(Object a, double nativeTime) throws Exception {
        System.err.println("Run test:");
        final IPropertyAccessor acc = ASMAccessorFactory.getAccessor(a.getClass());
        final String[] fields = getProps(a.getClass());
        final Method[] methods = getGetters(a.getClass());
        final int cnt = CALLS / fields.length;
        int res = 0;
        long[] asmAvg = new long[ROUNDS];
        long[] refl = new long[ROUNDS];
        for (int j = 0; j < ROUNDS; j++) {
            long t = System.currentTimeMillis();
            for (int i = 0; i < cnt; i++) {
                for (String p : fields)
                    res += System.identityHashCode(acc.get(a, p));
            }
            asmAvg[j] = System.currentTimeMillis() - t;

            t = System.currentTimeMillis();
            for (int i = 0; i < cnt; i++) {
                for (Method m : methods)
                    res += System.identityHashCode(m.invoke(a, (Object[]) null));
            }
            refl[j] = System.currentTimeMillis() - t;
        }
        System.out.println(res);
        System.out.println("Native avg: " + nativeTime);
        System.out.println("ASM avg: " + getAvg(asmAvg) + " - " + getAvg(asmAvg) / nativeTime);
        System.out.println("Refl avg: " + getAvg(refl) + " - " + getAvg(refl) / nativeTime);
    }

    /**
     * @param class1
     * @return
     * @throws IntrospectionException
     */
    private static String[] getProps(Class<?> class1) throws IntrospectionException {
        List<String> f = new ArrayList<String>();
        for (PropertyDescriptor p : Introspector.getBeanInfo(class1).getPropertyDescriptors()) {
            // skip indexed properties
            if (p instanceof IndexedPropertyDescriptor)
                continue;
            final Method read = p.getReadMethod();
            if (read != null)
                f.add(p.getName());
        }
        return f.toArray(new String[0]);
    }

    private static Method[] getGetters(Class<?> class1) throws IntrospectionException {
        List<Method> f = new ArrayList<Method>();
        for (PropertyDescriptor p : Introspector.getBeanInfo(class1).getPropertyDescriptors()) {
            // skip indexed properties
            if (p instanceof IndexedPropertyDescriptor)
                continue;
            final Method read = p.getReadMethod();
            if (read != null)
                f.add(read);
        }
        return f.toArray(new Method[0]);
    }

    private static double getAvg(long[] values) {
        double sum = 0;
        long max = 0;
        for (long l : values) {
            sum += l;
            max = Math.max(max, l);
        }
        return (sum - max) / (values.length - 1);
    }

    private static void buildNative(Class c) throws Exception {
        final Method[] fields = getGetters(c);
        System.out.println("private static double nativeRun(" + c.getSimpleName() + " a){");
        System.out.println("final int cnt = CALLS / " + fields.length + ";");
        System.out.println("final long[] nativeAvg = new long[ROUNDS];");
        System.out.println("int res = 0;");
        System.out.println("for (int j = 0; j < ROUNDS; j++) {");
        System.out.println("long t = System.currentTimeMillis();");
        System.out.println("for (int i = 0; i < cnt; i++) {");
        for (Method p : fields)
            System.out.println("res += System.identityHashCode(a." + p.getName() + "());");
        System.out.println("}");
        System.out.println("nativeAvg[j] = System.currentTimeMillis() - t;");
        System.out.println("}");
        System.out.println("System.out.println(res);");
        System.out.println("return getAvg(nativeAvg);");
        System.out.println("}");
    }

    // native implementations
    private static double nativeRun(TheDom a) {
        final int cnt = CALLS / 9;
        final long[] nativeAvg = new long[ROUNDS];
        int res = 0;
        for (int j = 0; j < ROUNDS; j++) {
            long t = System.currentTimeMillis();
            for (int i = 0; i < cnt; i++) {
                res += System.identityHashCode(a.getCity());
                res += System.identityHashCode(a.getClass());
                res += System.identityHashCode(a.getCountry());
                res += System.identityHashCode(a.getFirstname());
                res += System.identityHashCode(a.getName());
                res += System.identityHashCode(a.getState());
                res += System.identityHashCode(a.getStreet());
                res += System.identityHashCode(a.getStreetNumber());
                res += System.identityHashCode(a.getZip());
            }
            nativeAvg[j] = System.currentTimeMillis() - t;
        }
        System.out.println(res);
        return getAvg(nativeAvg);
    }

    private static double nativeRun(JPanel a) {
        final int cnt = CALLS / 55;
        final long[] nativeAvg = new long[ROUNDS];
        int res = 0;
        for (int j = 0; j < ROUNDS; j++) {
            long t = System.currentTimeMillis();
            for (int i = 0; i < cnt; i++) {
                res += System.identityHashCode(a.getUI());
                res += System.identityHashCode(a.getUIClassID());
                res += System.identityHashCode(a.getAccessibleContext());
                res += System.identityHashCode(a.getActionMap());
                res += System.identityHashCode(a.getAlignmentX());
                res += System.identityHashCode(a.getAlignmentY());
                res += System.identityHashCode(a.getAncestorListeners());
                res += System.identityHashCode(a.getAutoscrolls());
                res += System.identityHashCode(a.getBackground());
                res += System.identityHashCode(a.getBaselineResizeBehavior());
                res += System.identityHashCode(a.getBorder());
                res += System.identityHashCode(a.getComponentCount());
                res += System.identityHashCode(a.getComponentPopupMenu());
                res += System.identityHashCode(a.getComponents());
                res += System.identityHashCode(a.getContainerListeners());
                res += System.identityHashCode(a.getDebugGraphicsOptions());
                res += System.identityHashCode(a.isDoubleBuffered());
                res += System.identityHashCode(a.isEnabled());
                res += System.identityHashCode(a.isFocusCycleRoot());
                res += System.identityHashCode(a.getFocusTraversalPolicy());
                res += System.identityHashCode(a.isFocusTraversalPolicyProvider());
                res += System.identityHashCode(a.isFocusTraversalPolicySet());
                res += System.identityHashCode(a.isFocusable());
                res += System.identityHashCode(a.getFont());
                res += System.identityHashCode(a.getForeground());
                res += System.identityHashCode(a.getGraphics());
                res += System.identityHashCode(a.getHeight());
                res += System.identityHashCode(a.getInheritsPopupMenu());
                res += System.identityHashCode(a.getInputVerifier());
                res += System.identityHashCode(a.getInsets());
                res += System.identityHashCode(a.getLayout());
                res += System.identityHashCode(a.isManagingFocus());
                res += System.identityHashCode(a.getMaximumSize());
                res += System.identityHashCode(a.getMinimumSize());
                res += System.identityHashCode(a.getName());
                res += System.identityHashCode(a.getNextFocusableComponent());
                res += System.identityHashCode(a.isOpaque());
                res += System.identityHashCode(a.isOptimizedDrawingEnabled());
                res += System.identityHashCode(a.isPaintingForPrint());
                res += System.identityHashCode(a.isPaintingTile());
                res += System.identityHashCode(a.getPreferredSize());
                res += System.identityHashCode(a.getRegisteredKeyStrokes());
                res += System.identityHashCode(a.isRequestFocusEnabled());
                res += System.identityHashCode(a.getRootPane());
                res += System.identityHashCode(a.getToolTipText());
                res += System.identityHashCode(a.getTopLevelAncestor());
                res += System.identityHashCode(a.getTransferHandler());
                res += System.identityHashCode(a.isValidateRoot());
                res += System.identityHashCode(a.getVerifyInputWhenFocusTarget());
                res += System.identityHashCode(a.getVetoableChangeListeners());
                res += System.identityHashCode(a.isVisible());
                res += System.identityHashCode(a.getVisibleRect());
                res += System.identityHashCode(a.getWidth());
                res += System.identityHashCode(a.getX());
                res += System.identityHashCode(a.getY());
            }
            nativeAvg[j] = System.currentTimeMillis() - t;
        }
        System.out.println(res);
        return getAvg(nativeAvg);
    }
}
